package com.iagent.spring;

import com.iagent.bean.BannerPrinter;
import com.iagent.factory.AbstractIagentFactory;
import com.iagent.factory.DefaultIagentFactory;
import com.iagent.factory.IagentConfiguration;
import com.iagent.json.JSONSupport;
import com.iagent.logging.Logger;
import com.iagent.plugins.Interceptor;
import com.iagent.request.RequestConfig;
import com.iagent.resovler.parameter.ParameterResolver;
import com.iagent.resovler.result.ResultResolver;
import com.iagent.spring.parser.MultipartResultResolver;
import com.iagent.spring.resolver.MultipartFileParameterResolver;
import com.iagent.util.ClassUtils;
import com.iagent.util.StringUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * <b>Iagent BeanDefinitionRegistryPostProcessor
 * IagentFactoryBean dependency in Spring IOC</b>
 */
public class IagentProxyBean implements InitializingBean, DisposableBean, EnvironmentAware {
 
    /**
     * The Bean name that is automatically configured by default
     */
    public static final String SPRING_BOOT_IAGENT_FACTORY_NAME = "iagentProxyFactoryAutoConfiguration";

    //this IagentConfiguration Object is Only one of Spring IOC
    private AbstractIagentFactory iagentFactory = null;

    private String[] basePackages;

    private String jsonSupport;

    private String logImpl;

    private Class<? extends Logger> logImplClass;

    private Class<? extends JSONSupport> jsonSupportClass;

    private RequestConfig requestConfig;

    /**
     * add result resolver
     * @since 1.1.0
     */
    private List<Class<? extends ResultResolver>> addResultResolvers = new ArrayList<>(4);

    /**
     * add parameter resolver
     * @since 1.1.0
     */
    private List<Class<? extends ParameterResolver>> addParameterResolvers = new ArrayList<>(4);

    /**
     * add interceptor class
     * @since 1.1.0
     */
    private List<Class<? extends Interceptor>> pluginClass;

    /**
     * @since 1.1.0
     */
    private Environment environment;

    /**
     * The switch of banner printer
     * @since 1.1.1
     */
    private Boolean bannerMode;

    public IagentProxyBean(){
        super();
    };

    public IagentProxyBean(String[] basePackages){
        this.basePackages = basePackages;
    }

    public void setBasePackages(String[] basePackages) {
        this.basePackages = basePackages;
    }

    public void setJsonSupport(String jsonSupport) {
        this.jsonSupport = jsonSupport;
    }

    public void setLogImpl(String logImpl) {
        this.logImpl = logImpl;
    }

    public Class<? extends Logger> getLogImplClass() {
        return logImplClass;
    }

    public void setLogImplClass(Class<? extends Logger> logImplClass) {
        this.logImplClass = logImplClass;
    }

    public Class<? extends JSONSupport> getJsonSupportClass() {
        return jsonSupportClass;
    }

    public void setJsonSupportClass(Class<? extends JSONSupport> jsonSupportClass) {
        this.jsonSupportClass = jsonSupportClass;
    }

    public void setRequestConfig(RequestConfig requestConfig) {
        this.requestConfig = requestConfig;
    }

    public void addResultResolvers(List<Class<? extends ResultResolver>> resultResolvers) {
        this.addResultResolvers.addAll(resultResolvers);
    }

    public void addParameterResolvers(List<Class<? extends ParameterResolver>> parameterResolvers) {
        this.addParameterResolvers.addAll(parameterResolvers);
    }

    public void setAddResultResolvers(List<Class<? extends ResultResolver>> addResultResolvers) {
        addResultResolvers(addResultResolvers);
    }

    public void setAddParameterResolvers(List<Class<? extends ParameterResolver>> addParameterResolvers) {
        addParameterResolvers(addParameterResolvers);
    }

    public void setPluginClass(List<Class<? extends Interceptor>> pluginClass) {
        this.pluginClass = pluginClass;
    }

    public AbstractIagentFactory getIagentFactory() {
        return iagentFactory;
    }

    public void setBannerMode(Boolean bannerMode) {
        this.bannerMode = bannerMode;
    }

    /**
     * Spring Bean Initialize
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() {
        if (iagentFactory != null) {
            // It means it has been initialized
            return;
        }
        if (basePackages != null) {
            initialize();
            IagentConfiguration configuration = new IagentConfiguration();
            configuration.setBasePackages(basePackages);

            if (this.bannerMode != null) {
                BannerPrinter bannerPrinter = new BannerPrinter(this.bannerMode.booleanValue());
                configuration.setBannerPrinter(bannerPrinter);
            }else {
                configuration.setBannerPrinter(new BannerPrinter());
            }

            if (StringUtils.isNotEmpty(logImpl)) {
                configuration.setLogImpl(logImpl);
            }

            if (StringUtils.isNotEmpty(jsonSupport)) {
                configuration.setJsonSupport(jsonSupport);
            }

            if (this.jsonSupportClass != null) {
                configuration.setJsonSupport(this.jsonSupportClass);
            }

            if (this.logImplClass != null) {
                configuration.setLogImpl(this.logImplClass);
            }

            if (requestConfig != null) {
                configuration.setDefaultRequestConfig(requestConfig);
            }

            // Register a third-party request result object parser
            for (Class<?> resolverClazz : this.addResultResolvers) {
                configuration.getAliasRegister().registerAlias(resolverClazz.getName(), resolverClazz);
            }
            // Register a third-party parameter object parser
            for (Class<?> resolverClazz : this.addParameterResolvers) {
                configuration.getAliasRegister().registerAlias(resolverClazz.getName(), resolverClazz);
            }

            if (pluginClass != null) {
                // add plugin
                for (Class<? extends Interceptor> clazz : this.pluginClass) {
                    configuration.getAliasRegister().registerAlias(ClassUtils.getClassPathByClass(clazz), clazz);
                }
            }

            iagentFactory = new DefaultIagentFactory(configuration);

            Set<String> keyList = iagentFactory.getConfiguration().getEnvironment().getProperties();
            for (String key : keyList) {
                String value = this.environment.getProperty(key);
                if (StringUtils.isNotEmpty(value)) {
                    iagentFactory.getConfiguration().getEnvironment().addProperty(key, value);
                }
            }

            iagentFactory.refreshEnv();
        }
        // If it is empty, the initialization is not loaded
    }

    /**
     * Spring Bean destroy
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {

    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    /**
     * initialize Spring result resolver and parameter resolver
     * @since 1.1.0
     */
    private void initialize() {
        if (ClassUtils.existsClassName("org.springframework.web.multipart.MultipartFile")) {
            // Register the MultipartFile object for the Spring Web package
            this.addResultResolvers.add(MultipartResultResolver.class);
            // MultipartFile object parameter parsing for Spring Web packages
            this.addParameterResolvers.add(MultipartFileParameterResolver.class);
        }
    }
}
